<?php

require_once "MongoModel.php";

const WS_IP = "0.0.0.0";
const WS_PORT = 9501;

const PROCESS_NAME = 'DH_WS';
const REDIS_HOST = '127.0.0.1';
const REDIS_PORT = 6379;
const REDIS_PWD = '';

//消息类型
const MSG_TYPE_OPEN = 1;  //ws客户端连接消息
const MSG_TYPE_SEND = 2;  //ws客户端发送消息
const MSG_TYPE_IDENTITY = 3; //ws客户端身份消息

//连接用户的身份
const NORMAL_IDENTITY = 1; //游客
const VIP_IDENTITY = 2; //登录后的用户
const CUSTOMER_PRE_IDENTITY = 3; //售前客服
const CUSTOMER_AFTER_IDENTITY = 4; //售后客服

//连接用户列表
const GUEST_LIST = 'guest_list';
const VIP_LIST = "vip_list";
const CUSTOMER_PRE_LIST = "customer_pre_list";
const CUSTOMER_AFTER_LIST = "customer_after_list";

//任务池  类型
const TASK_ADD_MONGO = "add_mongo";
const TASK_ADD_IDENTITY = "add_identity";
const TASK_DEL_IDENTITY = "del_identity";

//table 大小  （后期无法扩容，谨慎设置）
const TABLE_SIZE = 1024;

$table = new swoole_table(TABLE_SIZE);
$table->column('fd', swoole_table::TYPE_INT);
$table->column('type', swoole_table::TYPE_INT);
$table->column('data', swoole_table::TYPE_STRING, 255);
$table->create();

$server = new swoole_websocket_server(WS_IP, WS_PORT);

/**
 * SWOOLE 配置项
 */
$server->set([
    'worker_num' => 2,
    'heartbeat_idle_time' => 5*60,
    'task_worker_num' => 4,
]);

$server->table = $table;

$server->on('open', 'onOpen');

$server->on('task', 'onTask');

$server->on('message', 'onMessage');

$server->on('close', 'onClose');

$server->on('start', function () {
   swoole_set_process_name(PROCESS_NAME);    //设置进程名
});

/**
 * 客户端关闭
 * @param $server
 * @param $fd
 */
function onClose($server, $fd) {
    $data = [
        'fd' => $fd,
        'task_type' => TASK_DEL_IDENTITY,
    ];
    $server->task(serialize($data));
    echo "client {$fd} closed\n";
}

/**
 * 处理客户端连接后的操作
 * @param $server
 * @param $request
 */
function onOpen($server, $request)
{
    $fds = [];
    foreach ($server->connections as $fd) {
        if ($fd != $request->fd) {
            $fds[] = $fd;
        }
    }
    if ($fds) {
        $rand_key = array_rand($fds,1);
        $toUserFd = $fds[$rand_key];
    } else {
        $toUserFd = '';
    }

    $data = [
        'userFd'    => $request->fd,
        'toUserFd'  => $toUserFd,
        'type'      => MSG_TYPE_OPEN,
    ];

    $server->push($request->fd, json_encode($data));
    echo "server: handshake success with fd{$request->fd}\n";
}

/**
 * 处理客户端的消息
 * @param $server
 * @param $frame
 */
function onMessage($server, $frame)
{
    $requestData = json_decode($frame->data, true);

    if ($requestData['type'] == MSG_TYPE_IDENTITY) {
        $identityData = [
            'avatar' =>  '',
            'nickname' => '',
            'ip'  => '127.0.0.1',
            'create_at' => time(),
        ];
        $user_info = [
            'fd'   => $frame->fd,
            'type' => $requestData['identity_type'],
            'data' => $identityData,
            'task_type' => TASK_ADD_IDENTITY,
        ];
//        @setIdentity($server, $frame->fd, $user_info);
        $server->task(serialize($user_info));
        $data = [
            'userFd'    => $requestData['toUserFd'],
            'toUserFd'  => $requestData['userFd'],
            'type'      => MSG_TYPE_IDENTITY,
        ];
    } elseif ($requestData['type'] == MSG_TYPE_SEND) {
        try {
            $task_data = [
                'userFd' => $requestData['userFd'],
                'toUserFd' => $requestData['toUserFd'],
                'task_type' => TASK_ADD_MONGO,
                'msg' => $requestData['msg']
            ];
            $server->taskwait(serialize($task_data));
        } catch (Exception $e) {

        }
        $data = [
            'userFd'    => $requestData['toUserFd'],
            'toUserFd'  => $requestData['userFd'],
            'type'      => MSG_TYPE_SEND,
            'msg'       => $requestData['msg'],
        ];
    }
    if ($requestData['toUserFd'])
        $server->push(intval($requestData['toUserFd']), json_encode($data));
    //$server->push(intval($requestData['userFd']), $frame->data);
}

/**
 * 处理任务池
 * @param $serv
 * @param $taskId
 * @param $workerId
 * @param $data
 * @throws \MongoDB\Driver\Exception\Exception
 */
function onTask($serv, $taskId, $workerId, $data)
{
    $req = unserialize($data);
    if ($req)
    {
        switch($req['task_type'])
        {
            case TASK_ADD_MONGO:
                @insert_mongodb($req['userFd'], $req['toUserFd'], $req['msg']);
                break;
            case TASK_ADD_IDENTITY:
                unset($req['task_type']);
                @setIdentity($serv, $req['fd'], $req);
                break;
            case TASK_DEL_IDENTITY:
                $type = fdToType($serv, $req['fd']);
                @delIdentity($serv, $req['fd'], $type);
                break;
            default:
                break;
        }
    }
}

/**
 * 暂未使用
 * @param $server
 * @param $fd
 * @param $code
 * @param $data
 */
function sendErrorMsg($server, $fd, $code, $data){
    sendJson($server, $fd, ['type' => $code, 'msg' => $data]);
}

/**
 * 发送数据给客户端
 * @param $server
 * @param $fd
 * @param $data
 */
function sendJson($server,$fd, $data)
{
    $msg = json_encode($data);
    if ($server->push(intval($fd), $msg) === false)
    {
        $server->close(intval($fd));
    }
}

/**
 * 设置连接用户的身份
 * @param $type
 */
function setIdentity($server, $fd, $data) {
    $type_list = typeToList($data['type']);
    $redis = new Redis();
    $redis->connect(REDIS_HOST, REDIS_PORT);
    $redis->sAdd($type_list, $fd);
    $redis->close();
    $server->table->set((string)$fd, $data);
}

/**
 * 客户端关闭后   删除登录信息
 * @param $server
 * @param $fd
 */
function delIdentity($server, $fd, $type) {
    $type_list = typeToList($type);
    $redis = new Redis();
    $redis->connect(REDIS_HOST, REDIS_PORT);
    $redis->sRem($type_list, $fd);
    $redis->close();
    $server->table->del((string)$fd);
}

/**
 * 根据用户的type, 获取用户列表的类型;
 * @param $type
 * @return string
 */
function typeToList($type) {
    switch ($type) {
        case VIP_IDENTITY:
            $type_list = VIP_LIST;
            break;
        case CUSTOMER_PRE_IDENTITY:
            $type_list = CUSTOMER_PRE_LIST;
            break;
        case CUSTOMER_AFTER_IDENTITY:
            $type_list = CUSTOMER_AFTER_LIST;
            break;
        case NORMAL_IDENTITY:
            $type_list = GUEST_LIST;
            break;
        default:
            $type_list = GUEST_LIST;
    }
    return $type_list;
}

/**
 * 获取一个客服
 */
function getCustomerOne() {

}

/**
 * 获取在线人数2
 * @param $type
 */
function getNumbers($type) {

}

/**
 *获取聊天记录
 */
function getHistory() {

}

/**
 * 添加聊天记录
 */
function addHistory() {

}

/**
 * 获取单个用户信息
 */
function getUser() {

}

/**
 * 根据fd获取在线用户的类型
 * @param $fd
 */
function fdToType($server, $fd) {
    return $server->table->get((string)$fd, 'type');
    //return $info['type'];
}

/**
 * 广播消息给客服
 * @param $data
 */
function broadcast($data) {

}

/**
 * 存储消息
 * @param $userFd
 * @param $toUserFd
 * @param $msg
 */
function add_msg($userFd, $toUserFd, $msg){

}

/**
 * 将聊天记录写入MongoDB
 * @param $userFd
 * @param $toUserFd
 * @throws \MongoDB\Driver\Exception\Exception
 */
function insert_mongodb($userFd, $toUserFd, $msg) {
    require_once "MongoModel.php";

    $mongo_client = new MongoModel();

    $filter = ['name' => 'content_id'];
    $options = [];
    $namespace_ids = "swoole.ids";
    $res = $mongo_client->select($filter, $options, $namespace_ids);
    $old_id = $res[0]->id;
    $document = [
        '_id' => new MongoDB\BSON\ObjectID,
        'id' => $old_id,
        'userFd' => intval($userFd),
        'toUserFd' => intval($toUserFd),
        'msg'   => $msg,
        'create_at' => time(),
    ];

    $namespace_content = "swoole.contents";

    $res = $mongo_client->add($document, $namespace_content);
    if ($res) {
        $new_id = $old_id + 1;
        $data = ['$set' => ['id' => $new_id]];
        $options_update = ['multi' => false, 'upsert' => false];
        $mongo_client->update($filter, $data, $options_update, $namespace_ids);
    }

}

$server->start();
